JNI 之 Libjpeg 分析
关于为什么使用 Libjpeg 来压缩图片的原因可以看 REASON.md,skia 引擎也是基于 libjpeg 的封装
libjpeg 的 Android jni 库地址:https://github.com/bither/bither-android-lib,可以直接将该库的 jni 文件夹直接拷贝至项目下,通过 cmake 引入构建。
案例
下面介绍的两个库都是基于 bither-android-lib 来实现的。
1、Luban
luban 这个库大家用的还是比较多的,但 luban 是基于 java 层面去压缩的,主要就是对 bitmap 的 options.inSampleSize 进行一个算法计算,具体可以查看源码 computeSize 方法。
而在 luban 的项目中,其实还有另一个通过 libjpeg 来实现的的压缩库,在 luban 的 turbor 分支中,但该分支中只有编译好后的 so 文件和项目使用,具体的源码是在 Luban-Turbo 项目中,我们可以来看下他的项目目录:
介绍:
1、include 文件夹就是开头指的 bither-android-lib 项目下面的 jni 文件
2、turbo.c 是我们自己写的 jni 接口,通过调用 include 中的 API,计算好结果后然后回调给 java 层
3、CMakeLists.txt 编译 cpp 文件
我们先来看下 CmakeLists.txt(cmake 的具体介绍可以看 cmake 必知):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24cmake_minimum_required(VERSION 3.4.1)
// 指定要编译的库 luban,并将指定 turbo.c 文件
add_library( luban
SHARED
src/main/cpp/turbo.c )
// 将头文件添加到搜索路径中
include_directories(src/main/cpp/include)
// 指定要编译的库 jpeg
add_library( jpeg SHARED IMPORTED )
// 设置库的一些属性
set_target_properties( jpeg
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpeg-turbo.a )
// 添加 Android log 库
find_library( log-lib log )
// 将库与其他库相关联
target_link_libraries( luban
jpeg
jnigraphics // 这个地方需要注意一下这个库,这个库是可以直接操作 Android Bitmap 信息的
${log-lib})
然后来看下 turbo.c 的主要片段代码,主要设置步骤如下
1、获取 Bitmap 相关的信息,例如 bitmap 的宽度、高度、像素点颜色值
具体可看代码 nativeCompress 方法的 65~86 行
2、将 Bitmap 中每个像素点的颜色值存储起来:
①、动态分配一块内存空间大小为 bitmap 的宽度高度3,这个 3 指的是 R、G、B
data = malloc((size_t) (width height 3));
②、pixelColor 像素缓存地址的指针,循环遍历 bitmap 的颜色值,并存储到 *data 中1
2
3
4
5
6
7
8
9
10
11
12
13
14for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
color = *((int *) pixelColor);
r = (BYTE) ((color & 0x00FF0000) >> 16);
g = (BYTE) ((color & 0x0000FF00) >> 8);
b = (BYTE) (color & 0X000000FF);
*data = b;
*(data + 1) = g;
*(data + 2) = r;
data += 3;
pixelColor += 4;
}
}
具体可看代码 nativeCompress 方法的 88~106 行
3、通过 libjpeg API 来压缩 *data 数据,并存储到一个文件中
具体代码可以查看 generateJpg 方法的 18~59 行 ,这个地方不再去阐述的原因是,在参考的很多项目中,这个部分的代码几乎都是一模一样,这个地方需要注意一下 jcs.optimize_coding = TRUE;
,由于设置成 true,给压缩的质量提高了 5~10 倍的效果,具体可以看开题的 REASON.md
2、EffectiveBitmap
EffectiveBitmap 几乎和 Luban-Turbo 一模一样,只是代码变量换了一下而已,看了一下两个库的构建时间
说代码差不多雷同其实也没什么,主要是因为官方给过一份 turbo 的 example,人家就是这么写的,我们就是这么调用的,一样的能怪我嘛!
总结
libjpeg 仅支持将压缩的结果设置到一个文件中,不能返回一个 bitmap ,如果压缩的目标是一个 File 文件,可以将文件先 BitmapFactory.decodeFile(filePath, options);
转成 bitmap,然后调用 native 方法,这个是从上述的两个案例中参考得出的,官方还提供了解压缩的方法,具体看以看 example 中 read_JPEG_file 函数。
参考文章:
- Android JNI 之 Bitmap 操作 https://juejin.im/post/5b5810a56fb9a04f8c5ee296
- Android NDK 开发之 CMake 必知必会 https://juejin.im/post/5b9879976fb9a05d330aa206